/*
 * Proto Streamer
 * Copyright (C) 2009 John Pritchard.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 */
package p.s.m;

import p.s.Request;

import java.io.IOException;
import java.net.URI;

/**
 * An application is defined by method and service classes.  
 * 
 * The role of method classes is purely organizational.  They define
 * the {@link Mux} instance and {@link Service} class.
 * 
 * The {@link Mux} instance is defined as an application singleton.
 * An application registers its message types on its {@link Mux}
 * singleton before its http methods are called.
 * 
 * Method classes define an initial service class in their
 * constructors, and can update this definition of the service at any
 * time by defining the session class field, and then calling the
 * setService method.
 * 
 * @author jdp
 */
public abstract class Session
    extends p.s.Abstract
{

    protected volatile boolean open;
    protected final Object sessionMonitor = new Object();
    protected volatile Class<? extends Service> sessionClass;
    protected volatile URI methodIdentifier, sessionIdentifier;
    protected volatile Service session;


    public Session(){
        super();
        this.open = true;
        this.sessionClass = p.s.q.Queue.class;
    }


    public void init(Request request)
        throws IOException
    {
        request.disableSocketTimeout();
        super.init(request);
        this.setStatusOk();
    }
    public final boolean isOpen(){
        return this.open;
    }
    public final void setOpen(boolean b){
        this.open = b;
    }
    protected abstract void enter(Service session);

    protected abstract void exit(Service session);

    protected Service lookupService(URI identifier){

        Class<? extends Service> sessionClass = this.sessionClass;
        if (p.s.q.Queue.class.isAssignableFrom(sessionClass))
            return p.s.q.Manager.Get(identifier,sessionClass);
        else if (p.s.t.Slot.class.isAssignableFrom(sessionClass))
            return p.s.t.Map.Get(identifier,sessionClass);
        else
            return null;
    }

    /**
     * Applications change their session service by defining the
     * session class field and then calling this method with a session
     * identifier.  The initial session identifier is the request line
     * uri.
     */
    public final Service setService(URI identifier){
        if (null == this.methodIdentifier)
            this.methodIdentifier = identifier;

        synchronized(this.sessionMonitor){
            this.sessionIdentifier = identifier;
            Service session = this.session;
            if (null != session){
                this.exit(session);
            }
            session = this.lookupService(identifier);
            if (null != session){
                this.enter(session);
                return session;
            }
            else
                throw new IllegalStateException("Unrecognized service class "+this.sessionClass.getName());
        }
    }
}
